use crate::plugin::JsValue;
use deno_runtime::deno_broadcast_channel::deno_broadcast_channel;
use deno_runtime::deno_cache::deno_cache;
use deno_runtime::deno_console::deno_console;
use deno_runtime::deno_core::{error::AnyError, url::Url, JsRuntime, ModuleId, PollEventLoopOptions, RuntimeOptions};
use deno_runtime::deno_crypto::deno_crypto;
use deno_runtime::deno_fetch::deno_fetch;
use deno_runtime::deno_ffi::deno_ffi;
use deno_runtime::deno_fs::{deno_fs, RealFs};
use deno_runtime::deno_http::deno_http;
use deno_runtime::deno_io::deno_io;
use deno_runtime::deno_napi::deno_napi;
use deno_runtime::deno_net::deno_net;
use deno_runtime::deno_node::deno_node;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_tls::deno_tls;
use deno_runtime::deno_url::deno_url;
use deno_runtime::deno_web::deno_web;
use deno_runtime::deno_webidl::deno_webidl;
use deno_runtime::deno_websocket::deno_websocket;
use deno_runtime::deno_webstorage::deno_webstorage;
use deno_runtime::{deno_core, runtime};
use std::borrow::Cow;
use std::sync::Arc;

pub struct Script {
    js_code: String,
    args: Vec<String>,
    result: JsValue,
    runtime: JsRuntime,
    last_rid: u32,
}

// static BOOTSTRAP_OPTIONS: WorkerOptions = WorkerOptions {
//     bootstrap: BootstrapOptions {
//         deno_version: "0.316.0".to_string(),
//         log_level: Default::default(),
//         unstable_features: vec![],
//         user_agent: "vch_js_runtime".to_string(),
//         inspect: false,
//         has_node_modules_dir: false,
//         ..Default::default()
//     },
//     extensions: vec![runtime::init_ops_and_esm()],
//     ..Default::default()
// };

#[derive(Debug)]
struct ResultResource {
    json_value: JsValue,
}

impl Script {
    const SCRIPT_NAME: &'static str = "";


    pub(crate) fn new(js_code: String, args: Vec<String>) -> Self {
        Self {
            js_code,
            args,
            result: Default::default(),
            runtime: JsRuntime::try_new(RuntimeOptions {
                extensions: vec![deno_webidl::init_ops_and_esm()
                                 , deno_console::init_ops_and_esm()
                                 , deno_url::init_ops_and_esm(), deno_tls::init_ops_and_esm()
                                 , deno_web::init_ops_and_esm::<PermissionsContainer>(Arc::new(Default::default()), None)
                                 , deno_fetch::init_ops_and_esm(Default::default())
                                 , deno_cache::init_ops_and_esm(None)
                                 , deno_websocket::init_ops_and_esm("".to_string(), None, None)
                                 , deno_webstorage::init_ops_and_esm(None)
                                 , deno_crypto::init_ops_and_esm(None)
                                 , deno_broadcast_channel::init_ops_and_esm(Default::default())
                                 , deno_node::init_ops_and_esm(Default::default(), Arc::new(RealFs))
                                 , deno_ffi::init_ops_and_esm()
                                 , deno_net::init_ops_and_esm(Default::default(), None)
                                 , deno_napi::init_ops_and_esm()
                                 , deno_http::init_ops_and_esm()
                                 , deno_io::init_ops_and_esm(Default::default())
                                 , deno_fs::init_ops_and_esm(Arc::new(RealFs))
                                 , runtime::init_ops_and_esm()],
                ..Default::default()
            }).expect("Failed to create runtime"),
            last_rid: 0,
        }
    }

    pub(crate) async fn run_js(&mut self) -> Result<(), AnyError> {
        self.runtime.load_main_es_module_from_code(&Url::parse(Self::SCRIPT_NAME)
            .expect("Invalid URL"), self.js_code.clone()).await.expect("TODO: panic message");
        // self.runtime.execute_script(Self::SCRIPT_NAME, &self.js_code)?;
        // self.runtime.op_metadata("op_return");
        // let state = self.runtime.op_state();
        // let mut state_lock = state.borrow_mut();
        // let table = &mut state_lock.resource_table;
        // let entry = table.take(0).expect("No resource table entry");

        let result = self.runtime.mod_evaluate(self.last_rid as ModuleId);
        self.runtime.run_event_loop(PollEventLoopOptions::default()).await?;
        let result = result.await?;
        Ok(result)
    }

    // #[op2()]
    // fn op_return(
    //     state: &mut OpState,
    //     args: JsValue,
    //     _buf: Option<JsBuffer>,
    // ) -> Result<JsValue, AnyError> {
    //     let entry = ResultResource { json_value: args };
    //     let resource_table = &mut state.resource_table;
    //     let _rid = resource_table.add(entry);
    //     //assert_eq!(rid, self.last_rid);
    //
    //     Ok(serde_json::Value::Null)
    // }
}

impl deno_core::Resource for ResultResource {
    fn name(&self) -> Cow<str> {
        "__rust_Result".into()
    }
}